


#include <getopt.h>
#include <time.h>
#include "isp.h"
#include "utest.h"



typedef enum {

    opt_help = 0,
    opt_loglevel,
    opt_width,
    opt_height,
    opt_mode,
    opt_input,
    opt_output,
    opt_number,
    opt_verbose,
    opt_format,
} options_e;

typedef enum {
    mode_gen_seq_4k,
    mode_gen_seq_v2,
    mode_gen_bmp_ca,
    mode_gen_bmp_ra,
    mode_parse_seq_4k,
    mode_parse_seq_v2,
    mode_parse_bmp_ca,
    mode_parse_bmp_ra,
    mode_export_seq_4k,
    mode_export_seq_v2,
    mode_export_bmp_ca,
    mode_export_bmp_ra,
    mode_frame_detect,
} mode_e;

typedef struct {
    qe_dim2 dim;
    mode_e mode;
    int number;
    int verbose;
    int format;
    char *file_input;
    char *file_output;
} argument_parser;

static int random_int(int min, int max)
{
    static qe_bool init = qe_false;

    if (init == qe_false) {
        time_t now;
        srand(time(&now));
        init = qe_true;
    }

    return rand() % max + min;
}

static qe_ret random_xpoint(struct bpc_xpoint *xp, int max_x, int max_y)
{
    xp->x = random_int(0, max_x);
    xp->y = random_int(0, max_y);
}

static void gen_bmp(argument_parser parser)
{
    int bit;
    char *buf;
    FILE *fp;
    time_t now;
    int memsize;
    int max_pix;
    bpc_xpoint_t xp;
    qe_u8 fmt;

    if (parser.number <= 0) {
        ut_warning("number 0");
        return;
    }

    if (parser.mode == mode_gen_bmp_ca) {
        fmt = BPTAB_BMP_CA;
    } else if (parser.mode == mode_gen_bmp_ra) {
        fmt = BPTAB_BMP_RA;
    } else {
        ut_warning("error mode %d", parser.mode);
        return;
    }

    max_pix = parser.dim.x * parser.dim.y;
    if (max_pix <= 0) {
        ut_warning("pixels 0");
        return;
    }
    if (parser.number >= max_pix) {
        ut_error("number %d > max %d", parser.number, max_pix);
        return;
    }

    ut_info("Generate BMP Test");
    ut_info("width  :%d", parser.dim.x);
    ut_info("height :%d", parser.dim.y);
    ut_info("number :%d", parser.number);
    ut_info("format :%s", (fmt==BPTAB_BMP_CA) ? "BMPCA" : "BMPRA");
    ut_info("output :%s", parser.file_output);

    memsize = max_pix / QE_BITS_PER_BYTE;
    buf = qe_malloc(memsize);
    if (!buf) {
        ut_error("malloc buf err");
        return;
    }
    memset(buf, 0x0, memsize);

    /* random generate */
    for (int i=0; i<parser.number; i++) {
        random_xpoint(&xp, parser.dim.x, parser.dim.y);
        bit = bpc_xp2bit(parser.dim, xp, fmt);
        ut_debug("point:(%d,%d) bit:%d", xp.x, xp.y, bit);
        qe_set_bit(bit, (unsigned long *)buf);
    }

    if (parser.verbose) {
        qe_bitdump_debug(buf, max_pix);
    }

    fp = fopen(parser.file_output, "wb");
    if (!fp) {
        ut_error("create file %s err", parser.file_output);
        goto __exit;
    }

    fwrite(buf, memsize, 1, fp);
    fclose(fp);

    ut_info("generate %d points and save to file %s", parser.number, parser.file_output);

__exit:
    qe_free(buf);
}

static void parse_bmp(argument_parser parser)
{
    char *buf;
    FILE *fp;
    int memsize;
    int max_pix;
    int compute_size;
    char *compute_buf;
    bpc_desc_t desc;
    qe_u8 fmt;
    qe_ret ret;

    if (parser.mode == mode_parse_bmp_ca) {
        fmt = BPTAB_BMP_CA;
    } else if (parser.mode == mode_parse_bmp_ra) {
        fmt = BPTAB_BMP_RA;
    } else {
        ut_error("error mode %d", parser.mode);
        return;
    }

    max_pix = parser.dim.x * parser.dim.y;
    if (max_pix <= 0) {
        ut_error("error max pix %d", max_pix);
        return;
    }

    ut_info("Parse BMP Test");
    ut_info("width  :%d", parser.dim.x);
    ut_info("height :%d", parser.dim.y);
    ut_info("number :%d", parser.number);
    ut_info("format :%s", (fmt==BPTAB_BMP_CA) ? "BMPCA" : "BMPRA");
    ut_info("input  :%s", parser.file_output);

    memsize = max_pix / QE_BITS_PER_BYTE;
    buf = qe_malloc(memsize);
    if (!buf) {
        ut_error("alloc mem error");
        return;
    }

    compute_size = 1024*1024*1;
    compute_buf = qe_malloc(compute_size);
    if (!compute_buf) {
        ut_error("alloc compbuf error");
        goto __exit;
    }

    fp = fopen(parser.file_input, "rb");
    if (!fp) {
        ut_error("open file %s err", parser.file_input);
        goto __exit;
    }

    fread(buf, memsize, 1, fp);
    fclose(fp);

    if (parser.verbose) {
        qe_bitdump_debug(buf, max_pix);
    }

    memset(&desc, 0x0, sizeof(desc));
    bpc_desc_init(&desc, parser.dim, fmt, compute_buf, compute_size);
    ret = bpc_bptab_parse(&desc, buf);
    if (ret != qe_ok) {
        ut_error("bptab parse err:%d", ret);
        goto __exit;
    } else {
        ut_info("bptab parse %d pixels", desc.num_static);
    }

__exit:
    qe_free(buf);
    qe_free(compute_buf);
}

static void export_bmp(argument_parser parser)
{

}

static void frame_detect(argument_parser parser)
{
    FILE *fp;
    int memsize;
    int num_points;
    char *buf;
    qe_list plist;
    bpc_desc_t desc;
    qe_ret ret;
    int compute_size;
    char *compute_buf;
    
    ut_info("Frame Detect Test");
    ut_info("width  :%d", parser.dim.x);
    ut_info("height :%d", parser.dim.y);
    ut_info("number :%d", parser.number);
    switch (parser.format) {
    case BPTAB_SEQ_4K: ut_info("format :SEQ4K");break;
    case BPTAB_SEQ_8K: ut_info("format :SEQ8K");break;
    case BPTAB_SEQ_V2: ut_info("format :SEQV2");break;
    case BPTAB_BMP_CA: ut_info("format :BMPCA");break;
    case BPTAB_BMP_RA: ut_info("format :BMPRA");break;
    }
    ut_info("input  :%s", parser.file_output);

    buf = qe_malloc(memsize);
    if (!buf) {
        ut_error("alloc frame mem error");
        return;
    }

    compute_size = 1024*1024*1;
    compute_buf = qe_malloc(compute_size);
    if (!compute_buf) {
        ut_error("alloc compbuf error");
        goto __exit;
    }

    fp = fopen(parser.file_input, "rb");
    if (!fp) {
        ut_error("open file %s err", parser.file_input);
        goto __exit;
    }

    fread(buf, memsize, 1, fp);
    fclose(fp);

    memset(&desc, 0x0, sizeof(desc));
    qe_list_init(&plist);
    bpc_desc_init(&desc, parser.dim, parser.format, compute_buf, compute_size);
    num_points = bpc_frame_detect(&desc, buf, 5, 60, &plist);
    ut_info("detect %s points", num_points);

__exit:
    qe_free(buf);
    qe_free(compute_buf);
}

static void test_run(argument_parser parser)
{
    switch (parser.mode) {

    case mode_gen_bmp_ca:
    case mode_gen_bmp_ra:
        gen_bmp(parser);
        break;

    case mode_parse_bmp_ca:
    case mode_parse_bmp_ra:
        parse_bmp(parser);
        break;

    case mode_export_bmp_ca:
    case mode_export_bmp_ra:
        export_bmp(parser);
        break;

    case mode_frame_detect:
        frame_detect(parser);
        break;
    }
}

static void usage(void)
{
    printf("bpc-test usage\n");
    printf("  -h,-? --help      help information\n");
    printf("  -l --loglevel     set log level(0~5)\n");
    printf("  -w --width        set pixel width\n");
    printf("  -h --height       set pixel height\n");
    printf("  -i --input        input filepath\n");
    printf("  -o --output       output filepath\n");
    printf("  -m --mode         testmode\n");
    printf("                    0:  generate SEQ4K table\n");
    printf("                    1:  generate SEQV2 table\n");
    printf("                    2:  generate BMPCA table\n");
    printf("                    3:  generate BMPRA table\n");
    printf("                    4:  parse SEQ4K table\n");
    printf("                    5:  parse SEQV2 table\n");
    printf("                    6:  parse BMPCA table\n");
    printf("                    7:  parse BMPRA table\n");
    printf("                    8:  export SEQ4K table\n");
    printf("                    9:  export SEQV2 table\n");
    printf("                    10: export BMPCA table\n");
    printf("                    11: export BMPRA table\n");
    printf("                    12: frame detect\n");
    printf("  -n --number       pixel number\n");
    printf("  -v --verbose      verbose mode\n");
    printf("  -f --format       bptab format\n");
    printf("                    0: SEQ4K\n");
    printf("                    1: SEQ8K\n");
    printf("                    2: SEQV2\n");
    printf("                    3: BMPCA\n");
    printf("                    4: BMPRA\n");
    printf("\n");
}

int main(int argc, char *argv[]) 
{
    int opt;
    qe_u8  loglevel;
    argument_parser arg_parser = {
        .dim.x       = 640,
        .dim.y       = 512,
        .mode        = mode_gen_seq_v2,
        .number      = 0,
        .verbose     = 0,
        .file_input  = "bpc-test-input.bin",
        .file_output = "bpc-test-output.bin"
    };


    qelog_init(QELOG_DEBUG, 
        QELOG_FUNC|QELOG_LV|QELOG_DM|QELOG_CL);


    static const struct option long_opts[] = {
        {"help",     no_argument,       NULL,  opt_help},
        {"loglevel", required_argument, NULL,  opt_loglevel},
        {"width",    required_argument, NULL,  opt_width},
        {"height",   required_argument, NULL,  opt_height},
        {"mode",     required_argument, NULL,  opt_mode},
        {"input",    required_argument, NULL,  opt_input},
        {"output",   required_argument, NULL,  opt_output},
        {"number",   required_argument, NULL,  opt_number},
        {"verbose",  no_argument,       NULL,  opt_verbose},
        {"format",   required_argument, NULL,  opt_format},
        {NULL, 0, NULL, 0}
    };


    while ((opt = getopt_long(argc, argv, "-?w:h:i:o:n:m:v", long_opts, NULL)) != -1) {
        
        switch (opt) {
        
        case 'w':
        case opt_width:
            arg_parser.dim.x = atoi(optarg);
            break;

        case 'h':
        case opt_height:
            arg_parser.dim.y = atoi(optarg);
            break;

        case 'm':
        case opt_mode:
            arg_parser.mode = atoi(optarg);
            break;

        case 'i':
        case opt_input:
            arg_parser.file_input = optarg;
            break;

        case 'o':
        case opt_output:
            arg_parser.file_output = optarg;
            break;

        case 'n':
        case opt_number:
            arg_parser.number = atoi(optarg);
            break;
        
        case 'v':
        case opt_verbose:
            arg_parser.verbose = 1;
            break;

        case 'f':
        case opt_format:
            arg_parser.format = atoi(optarg);
            break;

        case '?':
        case '-':
        case opt_help:
        default:
            usage();
            exit(EXIT_SUCCESS);
        }
    }

#ifdef __linux__
    ut_info("linux");
#elif _WIN32
    ut_info("windows");
#endif

    test_run(arg_parser);
}